use serde::{ ser::Error, Deserialize, Serialize };
use serde_json;
use ts_rs::TS;

/// 策略数据
#[derive(Serialize, Deserialize, Debug, Clone, TS)]
#[ts(export)]
pub enum TradeStrategyData {
    Short(Short),
    Long(Long),
}

/// 短线空头策略数据
#[derive(Serialize, Deserialize, Debug, Clone, Default, TS)]
#[ts(export)]
pub struct Short {
    pub volume: i32,
    pub desc: String,
}

/// 长线多头策略数据
#[derive(Serialize, Deserialize, Debug, Clone, Default, TS)]
#[ts(export)]
pub struct Long {
    pub volume: i32,
    pub desc: String,
}


impl TradeStrategyData {
    pub fn tname(&self) -> &str {
        match self {
            TradeStrategyData::Short(_) => "Short",
            TradeStrategyData::Long(_) => "Long",
        }
    }
    pub fn serialize(&self) -> Result<String, serde_json::Error> {
        let mut map = serde_json::Map::new();
        map.insert(
            "type".into(),
            serde_json::Value::String(format!("com.fuda.trade.model.TradeStrategyData.{}", self.tname()))
        );

        match self {
            TradeStrategyData::Short(data) => {
                let data_map = serde_json::to_value(data)?;
                map.extend(data_map.as_object().unwrap().clone());
            }
            TradeStrategyData::Long(data) => {
                let data_map = serde_json::to_value(data)?;
                map.extend(data_map.as_object().unwrap().clone());
            }
           
        }

        serde_json::to_string(&map)
    }

    pub fn deserialize(json: &str) -> Result<TradeStrategyData, serde_json::Error> {
        let value: serde_json::Value = serde_json::from_str(json)?;
        let type_str = value["type"]
            .as_str()
            .ok_or_else(|| serde_json::Error::custom("Missing type field"))?;

        match type_str {
            "com.fuda.trade.model.TradeStrategyData.Short" => {
                let buy_auto_value = value
                    .as_object()
                    .ok_or_else(|| serde_json::Error::custom("Expected object"))?;
                Ok(
                    TradeStrategyData::Short(
                        serde_json::from_value(serde_json::Value::Object(buy_auto_value.clone()))?
                    )
                )
            }
            "com.fuda.trade.model.TradeStrategyData.Long" => {
                let sell_auto_value = value
                    .as_object()
                    .ok_or_else(|| serde_json::Error::custom("Expected object"))?;
                Ok(
                    TradeStrategyData::Long(
                        serde_json::from_value(serde_json::Value::Object(sell_auto_value.clone()))?
                    )
                )
            }
           
            _ => Err(serde_json::Error::custom("Unknown type")),
        }
    }
}


#[cfg(test)]
mod test {
    use super::*;
    use log::debug;

    #[tokio::test]
    async fn test_trade_strategy_data_serde() {
        pretty_env_logger::init_timed();
        debug!("debug No more records available; exit the loop");
        println!("print No more records available; exit the loop");

        let buy_auto = TradeStrategyData::Short(Short {
            volume: 500,
            desc: "Publish1@trade_strategy_data.rs".to_string(),
        });

        let json = buy_auto.serialize().unwrap();
        // {"type":"com.fuda.trade.model.TradeStrategyData.Short","volume":100,"desc":"Publish1@TradeStrategyDataUnitTest.kt"}
        // {"type":"com.fuda.trade.model.TradeStrategyData.Short","volume":500,"desc":"Publish1@trade_strategy_data.rs"}

        println!("serialized: \n{}", json);

        // Deserialize the JSON back into a TradeStrategyData
        let buy_auto_de: TradeStrategyData = TradeStrategyData::deserialize(&json).unwrap();

        // Check that the deserialized TradeStrategyData matches the original
        match (buy_auto, buy_auto_de) {
            (TradeStrategyData::Short(original), TradeStrategyData::Short(deserialized)) => {
                assert_eq!(original.volume, deserialized.volume);
                assert_eq!(original.desc, deserialized.desc);
            }
            _ => panic!("Deserialized TradeStrategyData does not match the original"),
        }
    }
}
